import os
from django.shortcuts import redirect
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
# Create your views here.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchWindowException, StaleElementReferenceException, NoSuchElementException, WebDriverException, TimeoutException
from selenium.webdriver.common.action_chains import ActionChains
import redis
import json
import time
import base64
from django.http import HttpResponse
from urllib.parse import unquote  # Python 3
import threading
import logging
from webdriver_manager.chrome import ChromeDriverManager


from dingtalk_service.yida_client import YidaClient
from yida_crack.models import YidaUser
from yida_crack.models_cache import Company
from yidaplus.helpers import ApiResponse

logger = logging.getLogger(__name__)
lock = threading.Lock()
refresh_condition = threading.Condition(lock)

r = redis.StrictRedis(host='10.8.0.83', port=6379, db=0, decode_responses=True)
admin_user_id = "301546025724261514"
driver_manager = {}
refresh_timer = None
thread = None
keep_alive_thread = None

# 创建driver实例
def get_webdriver_instance(corp_code: str,admin=False):
    global driver_manager
    if (driver_manager.get(corp_code).get("driver")):
        return driver_manager.get(corp_code)
    else:
        if not admin:
            return None
        is_debug = os.environ.get("IS_DEBUG")
        if not is_debug:
            print("生产环境")
            options = webdriver.ChromeOptions()
            options.add_experimental_option("detach", True)
            options.add_argument('--ignore-certificate-errors')
            options.add_argument("--headless")  # 确保无头模式
            options.add_argument("--disable-gpu")  # 某些版本的Chrome在无头模式下需要此设置
            proxy = "http://10.8.0.1:8080"
            options.add_argument(f'--proxy-server={proxy}')
            driver = webdriver.Remote(
                command_executor='http://10.8.0.87:4444/wd/hub', options=options
            )
            driver_manager[corp_code]["driver"] = driver
            return driver_manager[corp_code]
        else:
            print("开发环境")
            options = webdriver.ChromeOptions()
            options.add_experimental_option("detach", True)
            options.add_argument('--ignore-certificate-errors')
            # options.add_argument("--headless")  # 确保无头模式
            options.add_argument("--disable-gpu")  # 某些版本的Chrome在无头模式下需要此设置
            # 设置Mitmproxy的代理服务器地址和端口（默认是8080）
            proxy = "http://10.8.0.1:8080"
            options.add_argument(f'--proxy-server={proxy}')
            service = Service(ChromeDriverManager().install())
            driver = webdriver.Chrome(service=service, options=options)
            driver_manager[corp_code]["driver"] = driver
            return driver_manager[corp_code]
            


# 刷新页面
def refresh_browser(driver, imediate_refresh: bool = False):
    logger.info("准备刷新页面")
    with refresh_condition:  # 获取锁
        logger.info("正在刷新页面")
        if driver:
            try:
                if imediate_refresh:
                    driver.refresh()  # 刷新页面
                    logger.info("成功刷新页面")
                change_pagin(driver=driver)
                # 重新设定定时器（例如，每10分钟刷新一次）
                timer = threading.Timer(7200, refresh_browser, [driver, True])
                timer.start()
                return timer  # 返回定时器对象以便之后可以取消
            except Exception as e:
                logger.error("刷新页面失败")
                logger.error(e)


def lasync_users(corp_code):
    # logger.info("开始更新用户列表")
    corp_info = get_webdriver_instance(corp_code)
    driver = corp_info.get("driver")
    try:
        # logger.info(f"开始加载表格......")
        wait = WebDriverWait(driver, 60)  # 等待最长 10 秒
        table = wait.until(EC.presence_of_element_located(
            (By.XPATH, r'//*[@id="UserAccountAuth"]/div/div[2]/div/div/div[4]/div/div[3]/div/div[2]/div[2]/table')))
        rows = table.find_elements(By.TAG_NAME, "tr")
        # logger.info(f"表格加载成功......")
        for row in rows:
            # 获取行中的所有单元格
            parts = row.text.split('\n')
            if (parts):
                r.set(f"{corp_code}:{parts[0]}", parts[2])
        wait = WebDriverWait(driver, 60)  # 等待最长 10 秒
        current_page = wait.until(EC.presence_of_element_located((
            By.XPATH, r"//button[contains(@class, 'next-pagination-item') and contains(@class, 'next-current')]")))
        logger.info(f"同步第{current_page.text}页，共{len(rows)}条.....")
    except Exception as e:
        logger.error(e)
    try:
        next_page = wait.until(EC.element_to_be_clickable(
            (By.XPATH, r'//*[@id="UserAccountAuth"]/div/div[2]/div/div/div[4]/div/div[4]/div[2]/button[2]')))
        driver.execute_script("arguments[0].click();", next_page)
        # driver.execute_script("arguments[0].scrollIntoView();", next_page)
        # next_page.click()
    except Exception as e:
        logger.info("按钮无法点击，返回第一页")
        first_page = wait.until(EC.element_to_be_clickable(
            (By.XPATH, r'//*[@id="UserAccountAuth"]/div/div[2]/div/div/div[4]/div/div[4]/div[2]/div/button[1]')))
        driver.execute_script("arguments[0].click();", first_page)
        # driver.execute_script("arguments[0].scrollIntoView();", first_page)
        # first_page.click()
        logger.info("同步已完成")


# 保持链接
def keep_alive(driver, corp_code):
    with refresh_condition:  # 获取锁
        while True:
            try:
                print(driver.current_url)
                check_admin_status(driver,corp_code)
                refresh_condition.wait(timeout=30)
            except Exception as e:
                logger.error(e)
                setPlusStatus(corp_code,0)
                clear_webdriver_instance(driver, corp_code)
                break  # 如果出错或driver不存在则退出线程

def setPlusStatus(corp_code: str, status: int):
    # 根据status的值确定对应的状态描述
    if status == 0:
        status_desc = "已关闭"
    elif status == 1:
        status_desc = "开启中"
    elif status == 2:
        status_desc = "已开启"
    else:
        raise ValueError("Invalid status value")

    # 更新宜搭表单数据
    YidaClient.update_form_data_by_field(
        "FORM-1F5F3BD00298443AA032FBB34B87A34DQEP5",
        admin_user_id,
        {"textField_lvaz02py": corp_code},
        {"radioField_lvazpu52": status_desc}
    )


def clear_webdriver_instance(driver, corp_code: str):
    global refresh_timer, driver_manager
    try:
        if refresh_timer:
            refresh_timer.cancel()
        
        if driver:
            driver.quit()
                # 确保清理操作
            driver = None
        if thread:
            thread.join()

        if keep_alive_thread:
            keep_alive_thread.join()    

        # 确保相关的全局变量被清理
        if corp_code in driver_manager:
            del driver_manager[corp_code]
        logger.info("WebDriver实例已成功关闭")
    except Exception as e:
        logger.error(f"关闭WebDriver实例时发生错误: {e}")
    finally:
        # 更新状态为已关闭
        setPlusStatus(corp_code, 0)



@csrf_exempt
@require_http_methods(["GET"])
def add_admin(request, corp_code: str):
    global driver_manager
    result = YidaClient.get_form_data_by_search_field("FORM-1F5F3BD00298443AA032FBB34B87A34DQEP5",
                                             admin_user_id,
                                             [
                                                  {
                                                    "type": "TEXT",
                                                    "operator": "eq",
                                                    "componentName": "TextField",
                                                    "key": "textField_lvaz02py",
                                                    "value": corp_code
                                                  }
                                             ])
    if not result:
        return ApiResponse.fail("未找到客户信息")
    form_data = result[0].form_data
    corp_name = form_data.get("textField_lvaz02px")
    max_grant_count = form_data.get("numberField_lvaz02q0")
    driver_manager[corp_code] = {
        "corp_code":corp_code,
        "corp_name":corp_name,
        "max_grant_count":max_grant_count,
        "driver":None
    }

    corp_info = get_webdriver_instance(corp_code,True)
    driver = corp_info["driver"]
    time.sleep(2)
    try:
        driver.get(
            rf'https://{corp_code}.aliwork.com/platformManage/userManage')
        # 获取窗口尺寸
        size = driver.get_window_size()
        width = size['width']
        height = size['height']

        logger.info(f"Window size: width = {width}px, height = {height}px")
        driver.set_window_size(800, 600)
    except NoSuchWindowException as e:
        logger.error(e)
        logger.info("Window has been closed. Reinitializing driver.")
        clear_webdriver_instance(driver, corp_code)
        return ApiResponse.success(data="hello failed")  # 返回None或者其他标志以表示窗口已关闭
    except Exception as e:
        logger.error(e)
        clear_webdriver_instance(driver, corp_code)
        return ApiResponse.success(data="未知错误")  # 返回None或者其他标志以表示窗口已关闭
    wait = WebDriverWait(driver, 60)  # 等待最多10秒
    canvas = wait.until(EC.visibility_of_element_located(
        (By.CSS_SELECTOR, '.module-qrcode-code canvas')))
    canvas_base64 = driver.execute_script(
        "return arguments[0].toDataURL('image/png').substring(21);", canvas)
    image_data = base64.b64decode(canvas_base64)
    thread = threading.Thread(target=to_company_plat, args=(corp_info,))
    thread.start()
    return HttpResponse(image_data, content_type="image/png")


# 进入公司管理页面
def to_company_plat(corp_info):
    try:
        driver = corp_info.get("driver")
        corp_code = corp_info.get("corp_code")
        global refresh_timer

        # 设置状态为“开启中”在开始操作之前
        setPlusStatus(corp_code, 1)
        logger.info("正在尝试进入管理页面...")
        wait = WebDriverWait(driver, 60)  # 继续等待最多60秒
        company_target = wait.until(EC.element_to_be_clickable(
            (By.XPATH, rf"//td[text()='{corp_info['corp_name']}']")))
        time.sleep(1)
        driver.execute_script("arguments[0].click();", company_target)
        logger.info("正在加载管理页面......")

        check_admin_status(driver=driver, corp_code=corp_code)
        refresh_timer = refresh_browser(driver)
        
        # 启动保活线程
        keep_alive_thread = threading.Thread(
            target=keep_alive, args=[driver, corp_info["corp_code"]])
        keep_alive_thread.start()
    except Exception as err:
        logger.error(err)
        logger.info("超时未进入管理页面，将状态设置为已关闭")
        setPlusStatus(corp_code, 0)
        clear_webdriver_instance(driver=driver, corp_code=corp_info["corp_code"])



def check_admin_status(driver,corp_code):
    try:
        wait = WebDriverWait(driver, 60)  # 等待最多20秒扫码
        text_login_flag = wait.until(EC.presence_of_element_located(
            (By.XPATH, r"//div[contains(text(), '全部部门 ·')]")))
        if text_login_flag:
            setPlusStatus(corp_code, 2)
            logger.info(f"管理页面正常:{text_login_flag.text}")
        else:
            logger.info("未找到管理页面")
            setPlusStatus(corp_code,0)
    except:
        logger.info("超时未找到管理页面")
        setPlusStatus(corp_code,0)

# 切换分页


def change_pagin(driver):
    wait = WebDriverWait(driver, 30)  # 等待最多30秒扫码
    pagin_selector = wait.until(EC.element_to_be_clickable(
        (By.XPATH, r'//*[@id="UserAccountAuth"]/div/div[2]/div/div/div[4]/div/div[4]/div[3]/span[2]')))
    logger.info(f"当前页码>>>>>{pagin_selector.text}")
    if (pagin_selector.text != "40"):
        pagin_selector.click()
        logger.info("切换分页成功")
        pagin_40 = wait.until(EC.element_to_be_clickable(
            (By.XPATH, r'/html/body/div[5]/div/ul/li[4]')))
        pagin_40.click()

# 获取用户登录状态


def get_user_login_status(user_id):
    user_key = f"user:{user_id}"
    user_data = r.hgetall(user_key)

    if not user_data:
        logger.info(f"No user found with ID: {user_id}")
        return None

    # 假设登录状态存储在散列的'auth_status'字段
    auth_status = user_data.get(b'auth_status')

    if auth_status:
        # Redis存储的数据为bytes类型，需要解码
        auth_status = auth_status.decode("utf-8")
        return auth_status == "authorized"  # 或者根据实际情况调整条件判断

    return False

# 用户登录


def add_or_update_user(corp_info, user_id, login_increment=1):
    corp_code = corp_info.get("corp_code")
    driver = corp_info.get("driver")
    max_grant_count = corp_info.get("max_grant_count")
    score = r.zscore(f'{corp_code}:users_by_last_login', user_id)
    if (score is not None):
        r.zadd(f'{corp_code}:users_by_last_login', {user_id: time.time()})
        return True
    else:
        login_user_count = r.zcard(f'{corp_code}:users_by_last_login')
        n = login_user_count - max_grant_count
        if n >= 0:
            # 获取分数最低的n个用户的user_id
            least_recent_users = r.zrange(
                f'{corp_code}:users_by_last_login', 0, n, withscores=False)
            for revoke_user_id in least_recent_users:
                revoke_user_by_id(
                    driver=driver, user_id=revoke_user_id, corp_code=corp_code)
        push_to_grant_queue(user_id=user_id)
        return True
# 添加用户到授权队列


def push_to_grant_queue(user_id, operation="grant"):
    data = {
        "user_id": user_id,
        "operation": operation,
        "expire_time": time.time() + 10
    }
    # 将字典序列化为JSON字符串
    json_data = json.dumps(data)
    r.rpush("user_grant_queue", json_data)


def click_button_by_xpath(driver, xpath, retry_count=3):
    try:
        wait = WebDriverWait(driver, 10)  # 等待最多10秒
        button = wait.until(EC.element_to_be_clickable(
            (By.XPATH, xpath)))
        driver.execute_script("arguments[0].click();", button)
    except StaleElementReferenceException:
        logger.info("StaleElementReferenceException")
        if retry_count <= 0:
            return
        else:
            retry_count = retry_count - 1
            click_button_by_xpath(driver, xpath, retry_count)
    except TimeoutException:
        logger.info("TimeoutException")
        if retry_count <= 0:
            return
        else:
            retry_count = retry_count - 1
            click_button_by_xpath(driver, xpath, retry_count)
    except NoSuchElementException:
        logger.info("NoSuchElementException")
    except WebDriverException:
        logger.info("WebDriverException")
        if retry_count <= 0:
            return
        else:
            retry_count = retry_count - 1
            click_button_by_xpath(driver, xpath, retry_count)


def grant_user_by_id(corp_code: str, user_id: str,):
    corp_info = get_webdriver_instance(corp_code)
    driver = corp_info.get("driver")
    if add_or_update_user(corp_info=corp_info, user_id=user_id):
        try:
            click_button_by_xpath(
                driver=driver, xpath="//button[span[text()='冻结'] or span[text()='授权']]")
        except NoSuchWindowException:
            logger.info("Window has been closed. Reinitializing driver.")
            clear_webdriver_instance(driver, corp_code)
            return False  # 返回None或者其他标志以表示窗口已关闭
        return True


@csrf_exempt
@require_http_methods(["GET"])
def grant_user(request, corp_code: str, user_id: str,):
    res_data = "账号已授权"
    if grant_user_by_id(corp_code=corp_code, user_id=user_id):
        res_data = "授权成功"
    else:
        return ApiResponse.fail(message="授权失败")
    encoded_url = request.GET.get('redir_url')
    if encoded_url:
        redir_url = unquote(encoded_url)
        return redirect(redir_url)
    return ApiResponse.success(data=res_data)


@csrf_exempt
@require_http_methods(["GET"])
def grant_user_by_name(request, corp_code: str):
    user_name = request.GET.get('user_name')
    user_id = r.get(f"{corp_code}:{user_name}")
    if not user_id:
        try:
            yida_user = YidaUser.objects.get(f"{corp_code}:{user_name}")
            user_id = yida_user.dingtalk_user_id
            r.set(f"{corp_code}:{user_name}",user_id)
        except YidaUser.DoesNotExist:
            return ApiResponse.fail(message="用户不存在")
    res_data = "账号已授权"
    if grant_user_by_id(corp_code=corp_code, user_id=user_id):
        res_data = "授权成功"
    else:
        return ApiResponse.fail(message="授权失败")
    return ApiResponse.success(message=res_data)


@csrf_exempt
@require_http_methods(["GET"])
def revoke_user(request, corp_code: str, user_id: str):
    corp_info = get_webdriver_instance(corp_code)
    driver = corp_info.get("driver")
    if not revoke_user_by_id(driver=driver, user_id=user_id, corp_code=corp_code):
        return ApiResponse.success(data="冻结失败，请重试")  # 返回None或者其他标志以表示窗口已关闭
    return ApiResponse.success(data="冻结成功")


def revoke_user_by_id(driver, user_id, corp_code):
    try:
        push_to_grant_queue(user_id=user_id, operation="revoke")
        click_button_by_xpath(
            driver=driver, xpath="//button[span[text()='冻结'] or span[text()='授权']]")
        return True
        # button.click()
    except NoSuchWindowException:
        logger.info("Window has been closed. Reinitializing driver.")
        clear_webdriver_instance(driver, corp_code)
        return False
    except:
        return False


@csrf_exempt
@require_http_methods(["GET"])
def do_refresh_browser(request, corp_code: str):
    corp_info = get_webdriver_instance(corp_code)
    driver = corp_info.get("driver")
    refresh_browser(driver=driver, imediate_refresh=True)

# 同步用户列表


@csrf_exempt
@require_http_methods(["GET"])
def async_users(request, corp_code: str):
    count = 1
    r.delete(f"{corp_code}")
    def loop_async_users(count,retry=3):
        corp_info = get_webdriver_instance(corp_code)
        driver = corp_info.get("driver")
        wait = WebDriverWait(driver, 60)  # 等待最长 10 秒
        # 部门账号
        table = wait.until(EC.presence_of_element_located(
            (By.XPATH, r'//*[@id="UserAccountAuth"]//div[@class="next-table-body"]/table')))
        rows = table.find_elements(By.TAG_NAME, "tr")
        for row in rows:
            # 获取行中的所有单元格
            parts = row.text.split('\n')
            print(f"姓名：{parts[0]}  id：{parts[2]}")
            if (parts):
                r.set(f"{corp_code}:{parts[0]}", parts[2])
                try:
                    YidaUser.objects.update_or_create(
                        user_name=parts[0],
                        corp_code=corp_code,
                        defaults={
                            "dingtalk_user_id": parts[2],
                        }
                    )
                except Exception as e:
                    logger.error(e)
                # user_item = {
                #     "name": parts[0],
                #     "dingtalk_id":parts[2]
                # }
                # company = Company.load(corp_code)
                # company.add_or_update_user()
                # r.lpush(f"{corp_code}:user_list", json.dumps(user_item))
        logger.info(f"同步第{count}页，共{len(rows)}.....")
        try:
            # 下一页
            next_page = wait.until(EC.presence_of_element_located(
                (By.XPATH, r'//*[@id="UserAccountAuth"]//div[@class="next-pagination-pages"]/button[2]')))
            if next_page.is_enabled():
                next_page.click()
                time.sleep(2)
                count = count + 1
                loop_async_users(count)
                retry = 3
            else:
                raise Exception("按钮是禁用的")
        except Exception as e:
            logger.error(e)
            if retry > 0:
                loop_async_users(count,retry-1)
                return
            # 第一页
            first_page = wait.until(EC.element_to_be_clickable(
                (By.XPATH, r'//*[@id="UserAccountAuth"]//div[@class="next-pagination-list"]/button[1]')))
            first_page.click()
            logger.info("同步已完成")
    loop_async_users(count)
    return ApiResponse.success(data="同步成功")

@csrf_exempt
@require_http_methods(["POST"])
def add_user(request,corp_code:str):
    data:dict[str,any] = json.loads(request.body)
    user = data.get("user",{})
    if user:
        user_name = user.get('userName',"")
        user_id = user.get('userId',"")
        if user_name and user_id :
            key = f"{corp_code}:{user_name}"
            r.set(key, user_id)
            return ApiResponse.success(message='加入成功')
        
    return ApiResponse.fail(message='加入失败,请检查用户是否为空')

@csrf_exempt
@require_http_methods(["GET"])
def get_user_page_data(request, corp_code: str):
    # 分页参数
    page_size = int(request.GET.get('pageSize', 10))  # 每页显示的数量
    page_number = int(request.GET.get('currentPage', 1))  # 当前页码
    # 搜索参数
    search_user_name = request.GET.get('userName', None)
    search_user_id = request.GET.get('userId', None)


    filtered_keys = []
    # 使用 SCAN 命令来获取包含 corp_code 的键
    cursor, keys = r.scan(match=f"{corp_code}:*", count=500)

    # 获取这些键对应的值
    values = r.mget(keys)
    
    # 遍历并加入符合条件的值
    for key, value in zip(keys, values):
        # 如果值id符合要求，则加入到filtered_keys中
        if ('users_by_last_login' not in key 
            and (search_user_name is None or search_user_name in key) 
            and (search_user_id is None or search_user_id in value)):
            # 加入到filtered_keys中
            filtered_keys.append((key, value))
        
    # 如果有多个匹配项，继续迭代
    while cursor != 0:
        cursor, keys = r.scan(cursor=cursor, match=f"{corp_code}:*", count=500)         
        values = r.mget(keys)
            # 遍历并加入符合条件的值
        for key, value in zip(keys, values):
            # 如果值id符合要求，则加入到filtered_keys中            
            if ('users_by_last_login' not in key 
                and (search_user_name is None or search_user_name in key) 
                and (search_user_id is None or search_user_id in value)):
                # 加入到filtered_keys中
                filtered_keys.append((key, value))

    total_count = len(filtered_keys)  # 总数据量
    # 计算起始和结束索引
    start_index = (page_number - 1) * page_size
    end_index = start_index + page_size

    # 根据分页获取对应的数据
    data = []
    for tuple_item in filtered_keys[start_index:end_index]:
        key = tuple_item[0]
        value = tuple_item[1]
        user_name = key.split(':')[1]  # 从key中提取用户名        
        user_id = value
        data.append({"name": user_name, "userId": user_id})

    # 构造分页信息
    response_data = {
        "data": data,
        "currentPage": page_number,
        "totalCount": total_count
    }

    return ApiResponse.success(data=response_data)

@csrf_exempt
@require_http_methods(["GET"])
def get_plus_status(request, corp_code: str):
    try:
        plus_status = queryPlusStatus(corp_code=corp_code)
        if plus_status=="已开启":
            corp_info = get_webdriver_instance(corp_code)
            driver = corp_info["driver"]
            if driver and corp_code == corp_info["corp_code"]:
                return ApiResponse.success(plus_status)
            else:
                setPlusStatus(corp_code=corp_code,status=0)
                raise ApiResponse.fail(message="状态获取失败，请重新登录")
        else:
            return ApiResponse.success(plus_status)

    except Exception as e:
        # 可以打印 e 来获取更多的错误信息，或记录到日志
        setPlusStatus(corp_code=corp_code,status=0)
        return ApiResponse.fail(message="Plus服务未开启,请开启Plus服务")
    
@csrf_exempt
@require_http_methods(["POST"])
def close_plus_status(request,corp_code:str):
    try:
        corp_info = get_webdriver_instance(corp_code)
        driver = corp_info["driver"]

        clear_webdriver_instance(driver=driver,corp_code=corp_code)
    except Exception as e:
        logger.error(f"关闭WebDriver实例时发生错误: {e}")
        return ApiResponse.success(message="已经关闭了plus服务")
    finally:
        # 更新状态为已关闭
        setPlusStatus(corp_code, 0)
    return ApiResponse.success(message="关闭Plus服务成功")

def queryPlusStatus(corp_code: str):
    data=YidaClient.get_form_data_by_search_field(form_uuid="FORM-1F5F3BD00298443AA032FBB34B87A34DQEP5",
                                             userId=admin_user_id,
                                             search_field={
                                              "textField_lvaz02py":corp_code
                                              }
                                            )
    plus_status = data[0].form_data.get("radioField_lvazpu52","")
    return plus_status


@csrf_exempt
@require_http_methods(["POST"])
def regist_user(request, corp_code: str):
    req_data:dict[str,any] = json.loads(request.body)
    user_name = req_data.get("user_name")
    user_id = req_data.get("user_id")
    YidaUser.objects.update_or_create(corp_code=corp_code, user_name=user_name, defaults={
        "dingtalk_user_id":user_id
    })
    r.set(f"{corp_code}:{user_name}", user_id)
    return ApiResponse.success(message="注册成功")